Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
To demostrate how to do the gene clustering usign COTAN we begin importing the COTAN object that stores all elaborated data and, in this case, regarding a mouse embrionic cortex dataset (developmental stage E17.5).
input_dir = "Data/"
layers = list("L1"=c("Reln","Lhx5"), "L2/3"=c("Satb2","Cux1"), "L4"=c("Rorb","Sox5") , "L5/6"=c("Bcl11b","Fezf2") , "Prog"=c("Vim","Hes1"))
#objE17 = readRDS(file = paste(input_dir,"E17.5_cortex.cotan.RDS", sep = ""))
objE17 = readRDS(file = paste(input_dir,"E17_cortex_cl2.cotan.RDS", sep = ""))
[1] "calculating gene coexpression space: output tanh of reduced coex matrix"
L11 L12 L2/31 L2/32 L41 L42 L5/61 L5/62 Prog1 Prog2
"Reln" "Lhx5" "Satb2" "Cux1" "Rorb" "Sox5" "Bcl11b" "Fezf2" "Vim" "Hes1"
[1] "Get p-values on a set of genes on columns genome wide on rows"
[1] "Using function S"
[1] "function to generate S "
[1] "Secondary markers:181"
[1] "function to generate S "
[1] "Columns (V set) number: 181 Rows (U set) number: 1236"

Hierarchical clustering
par_pca = pca_1[colnames(g.space)[colnames(g.space) %in% rownames(pca_1)],]
#plot N 1
p1 <- ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Fezf2","hclust"],]), collapse = ", "),
face = "italic",
size =10,
color = "#3C5488FF")
#plot N 2
p2 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == unique(par_pca$hclust)[!unique(par_pca$hclust) %in% unique(par_pca[unlist(layers),"hclust"])][1],]), collapse = ", "),
face = "italic",
size =10,
color = "gray")
#plot N 3
p3 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == unique(par_pca$hclust)[!unique(par_pca$hclust) %in% unique(par_pca[unlist(layers),"hclust"])][2],]), collapse = ", "),
face = "italic",
size =10,
color = "gray")
#plot N 4
p4 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Reln","hclust"],]), collapse = ", "),
face = "italic",
size =10,
color = "#E64B35FF")
#plot N 5
p5 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Cux1","hclust"],]), collapse = ", "),
face = "italic",
size =10,
color = par_pca["Cux1","colors"])
#plot N 6
p6 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Rorb","hclust"],]), collapse = ", "),
face = "italic",
size =10,
color = par_pca["Rorb","colors"])
#plot N 7
p7 = ggparagraph(text = paste0(rownames(par_pca[par_pca$hclust == par_pca["Vim","hclust"],]), collapse = ", "),
face = "italic",
size =10,
color = par_pca["Vim","colors"])
w = ggparagraph(text = " ",
face = "italic",
size =10,
color = "white")
pp =ggarrange(p3,p5,p1,p2,p4,p6,p7,w,
ncol = 1, nrow = 8,
heights = c(0.1,0.15,0.23, 0.1, 0.2, 0.2, 0.1, 0.35))
lay <- rbind(c(1,NA),
c(1,2.5),
c(1,2.5),
c(1,2.5),
c(1,2.5),
c(1,2.5),
c(1,NA))
gridExtra::grid.arrange(tree, pp, layout_matrix = lay)

or just with primary markers
# some more genes as landmarks
controls =list("genes related to L5/6"=c("Foxp2","Tbr1"), "genes related to L2/3"=c("Mef2c"), "genes related to Prog"=c("Nes","Sox2") , "genes related to L1"=c() , "genes related to L4"=c())
dend %>%
dendextend::set("labels", ifelse(labels(dend) %in% rownames(pca_1)[rownames(pca_1) %in% c(unlist(layers),unlist(controls))], labels(dend), "")) %>%
dendextend::set("branches_k_color", value = c("gray80","#4DBBD5FF","#91D1C2FF" ,"gray80","#F39B7FFF","#E64B35FF","#3C5488FF"), k = 7) %>%
plot(horiz=F, axes=T,ylim = c(0,100))

Now we can plot the PCA
# dataframe to be able to label only primary markers and control genes
textdf <- pca_1[rownames(pca_1) %in% c(unlist(layers),unlist(controls)) , ]
for (m in c(1:length(controls))) {
for (g in controls[[m]]) {
if(g %in% rownames(textdf)){
textdf[g,"highlight"] = names(controls[m])
}
}
}
# deciding the colors
mycolours <- c("genes related to L5/6" = "#3C5488FF","genes related to L2/3"="#F39B7FFF","genes related to Prog"="#4DBBD5FF","genes related to L1"="#E64B35FF","genes related to L4" = "#91D1C2FF", "not marked"="#B09C85FF")
# to assing correcly the cluster number and the color
mycolours2 = c("Reln","Satb2","Rorb","Bcl11b","Vim")
names(mycolours2) = unique(cut[unlist(layers)])
mycolours2[mycolours2 == "Reln"] = "#E64B35FF"
mycolours2[mycolours2 == "Satb2"] = "#F39B7FFF"
mycolours2[mycolours2 == "Rorb"] = "#91D1C2FF"
mycolours2[mycolours2 == "Bcl11b"] = "#3C5488FF"
mycolours2[mycolours2 == "Vim"] = "#4DBBD5FF"
color_to_add = unique(pca_1$hclust)[!unique(pca_1$hclust) %in% as.numeric(names(mycolours2))]
names(color_to_add) = unique(pca_1$hclust)[!unique(pca_1$hclust) %in% as.numeric(names(mycolours2))]
color_to_add[color_to_add %in%
unique(pca_1$hclust)[!unique(pca_1$hclust) %in% as.numeric(names(mycolours2))]] = "#B09C85FF"
mycolours2 = c(mycolours2,color_to_add)
pca1 = ggplot(subset(pca_1,!hclust %in% unique(cut[unlist(layers)]) ), aes(x=PC1, y=PC2)) + geom_point(alpha = 0.3,color = "#B09C85FF",size=1)
#pca2 = pca1 + geom_point(data = subset(pca_1, highlight != "not marked" ), aes(x=PC1, y=PC2, colour=hclust),size=2.5,alpha = 0.9)
pca2 = pca1 + geom_point(data = subset(pca_1, hclust %in% unique(cut[unlist(layers)]) ), aes(x=PC1, y=PC2, colour=as.character(hclust)),size=1,alpha = 0.5) +
scale_color_manual( "Status", values = mycolours2) +
scale_fill_manual( "Status", values = mycolours2) +
xlab("") + ylab("") +
geom_label_repel(data =textdf , aes(x = PC1, y = PC2, label = rownames(textdf),fill=as.character(hclust)),
label.size = NA,
alpha = 0.5,
direction = "both",
na.rm=TRUE,
seed = 1234) +
geom_label_repel(data =textdf , aes(x = PC1, y = PC2, label = rownames(textdf)),
label.size = NA,
segment.color = 'black',
segment.size = 0.5,
direction = "both",
alpha = 1,
na.rm=TRUE,
fill = NA,
seed = 1234) +
ggtitle("PCA") +
theme_light(base_size=10) +
theme(axis.text.x=element_blank(),plot.title = element_text(size=14,
face="italic",
color="#3C5488FF",
hjust=0.01,
lineheight=1.2,margin = margin(t = 5, b = -15)),
axis.text.y=element_blank(),
legend.position = "none") # titl)
pca2 #+ geom_encircle(data = pca_1, aes(group=hclust))

t-SNE code and plot
# run the t-SNE
cl.genes.tsne = Rtsne(g.space ,initial_dims = 100, dims = 2, perplexity=30,eta = 200, verbose=F, max_iter = 3000,theta=0.4,num_threads = 10,pca_center = T, pca_scale = FALSE, normalize = T )
d_tsne_1 = as.data.frame(cl.genes.tsne$Y)
rownames(d_tsne_1) = rownames(g.space)
d_tsne_1 = d_tsne_1[order.dendrogram(dend),]
# save the cluster numebr inside a dataframe with the t-SNE information
d_tsne_1$hclust = cut
d_tsne_1$names = rownames(d_tsne_1)
# as before to label only some genes
textdf <- d_tsne_1[rownames(d_tsne_1) %in% c(unlist(layers),unlist(controls)),]
for (m in c(1:length(controls))) {
for (g in controls[[m]]) {
if(g %in% rownames(textdf)){
textdf[g,"highlight"] = names(controls[m])
}
}
}
p1 = ggplot(subset(d_tsne_1,!hclust %in% unique(cut[unlist(layers)])), aes(x=V1, y=V2)) + geom_point(alpha = 0.3, color = "#B09C85FF", size=1)
p2 = p1 + geom_point(data = subset(d_tsne_1, hclust %in% unique(cut[unlist(layers)]) ), aes(x=V1, y=V2, colour=as.character(hclust)),size=1,alpha = 0.5) +
scale_color_manual("Status", values = mycolours2) +
scale_fill_manual("Status", values = mycolours2) +
xlab("") + ylab("")+
geom_label_repel(data =textdf , aes(x = V1, y = V2, label = names,fill=as.character(hclust)),
label.size = NA,
alpha = 0.5,
direction = "both",
na.rm=TRUE,
seed = 1234) +
geom_label_repel(data =textdf , aes(x = V1, y = V2, label = names),
label.size = NA,
segment.color = 'black',
segment.size = 0.5,
direction = "both",
alpha = 1,
na.rm=TRUE,
fill = NA,
seed = 1234) +
ggtitle("t-SNE") +
theme_light(base_size=10) +
theme(axis.text.x=element_blank(),plot.title = element_text(size=14,
face="italic",
color="#3C5488FF",
hjust=0.01,
lineheight=1.2,margin = margin(t = 5, b = -15)),
axis.text.y=element_blank(),
legend.position = "none") # titl)
p2

Code to create an iteractive plot. This can be modified to be used with all the plots.
Multidimensional scaling (MDS) and plot

R version 4.0.4 (2021-02-15)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 18.04.5 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/libopenblasp-r0.2.20.so
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] grid stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggpubr_0.4.0 dendextend_1.14.0 MASS_7.3-53.1 htmlwidgets_1.5.3 forcats_0.5.1 stringr_1.4.0
[7] dplyr_1.0.4 purrr_0.3.4 readr_1.4.0 tidyr_1.1.2 tibble_3.0.6 tidyverse_1.3.0
[13] plotly_4.9.3 Rtsne_0.15 ggrepel_0.9.1 COTAN_0.1.0 factoextra_1.0.7 ggplot2_3.3.3
loaded via a namespace (and not attached):
[1] matrixStats_0.58.0 fs_1.5.0 lubridate_1.7.9.2 filelock_1.0.2 RColorBrewer_1.1-2
[6] httr_1.4.2 tools_4.0.4 backports_1.2.1 R6_2.5.0 DBI_1.1.1
[11] lazyeval_0.2.2 BiocGenerics_0.36.0 colorspace_2.0-0 GetoptLong_1.0.5 withr_2.4.1
[16] tidyselect_1.1.0 gridExtra_2.3 curl_4.3 compiler_4.0.4 cli_2.3.0
[21] rvest_0.3.6 Cairo_1.5-12.2 basilisk.utils_1.2.2 xml2_1.3.2 labeling_0.4.2
[26] scales_1.1.1 rappdirs_0.3.3 digest_0.6.27 foreign_0.8-81 rmarkdown_2.7
[31] rio_0.5.16 basilisk_1.2.1 pkgconfig_2.0.3 htmltools_0.5.1.1 dbplyr_2.1.0
[36] rlang_0.4.10 GlobalOptions_0.1.2 readxl_1.3.1 rstudioapi_0.13 gridGraphics_0.5-1
[41] farver_2.0.3 shape_1.4.5 generics_0.1.0 jsonlite_1.7.2 crosstalk_1.1.1
[46] zip_2.1.1 car_3.0-10 magrittr_2.0.1 Matrix_1.3-2 Rcpp_1.0.6
[51] munsell_0.5.0 S4Vectors_0.28.1 abind_1.4-5 reticulate_1.18 viridis_0.5.1
[56] lifecycle_0.2.0 stringi_1.5.3 yaml_2.2.1 carData_3.0-4 parallel_4.0.4
[61] crayon_1.4.0 lattice_0.20-41 haven_2.3.1 cowplot_1.1.1 circlize_0.4.12
[66] hms_1.0.0 knitr_1.31 ComplexHeatmap_2.6.2 pillar_1.4.7 rjson_0.2.20
[71] ggsignif_0.6.1 stats4_4.0.4 reprex_1.0.0 glue_1.4.2 evaluate_0.14
[76] data.table_1.13.6 modelr_0.1.8 png_0.1-7 vctrs_0.3.6 cellranger_1.1.0
[81] gtable_0.3.0 clue_0.3-58 assertthat_0.2.1 openxlsx_4.2.3 xfun_0.20
[86] broom_0.7.5 rstatix_0.7.0 viridisLite_0.3.0 IRanges_2.24.1 cluster_2.1.1
[91] ellipsis_0.3.1
LS0tCnRpdGxlOiAiR2VuZV9jbHVzdGVyaW5nIgpvdXRwdXQ6CiAgICBodG1sX25vdGVib29rOgogICAgICBjc3M6IGh0bWwtbWQtMDEuY3NzCiAgICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgICAgaGlnaGxpZ2h0OiBoYWRkb2NrCiAgICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICAgIHRoZW1lOiBzcGFjZWxhYgogICAgICB0b2M6IHllcwogICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgY29sbGFwc2VkOiBubwotLS0KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb2xsYXBzZSA9IFRSVUUsCiAgY29tbWVudCA9ICIjPiIsCiAgZmlnLndpZHRoID0gNywKICBmaWcuaGVpZ2h0ID0gNwopCmBgYAoKYGBge3Igc2V0dXB9CgpsaWJyYXJ5KGZhY3RvZXh0cmEpCmxpYnJhcnkoQ09UQU4pCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShSdHNuZSkKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoZGVuZGV4dGVuZCkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdncHVicikKYGBgCgpUbyBkZW1vc3RyYXRlIGhvdyB0byBkbyB0aGUgZ2VuZSBjbHVzdGVyaW5nIHVzaWduIENPVEFOIHdlIGJlZ2luIGltcG9ydGluZyB0aGUgQ09UQU4gb2JqZWN0IHRoYXQgc3RvcmVzIGFsbCBlbGFib3JhdGVkIGRhdGEgYW5kLCBpbiB0aGlzIGNhc2UsIHJlZ2FyZGluZyBhIG1vdXNlIGVtYnJpb25pYyBjb3J0ZXggZGF0YXNldCAoZGV2ZWxvcG1lbnRhbCBzdGFnZSBFMTcuNSkuCgpgYGB7cn0KaW5wdXRfZGlyID0gIkRhdGEvIgpsYXllcnMgPSBsaXN0KCJMMSI9YygiUmVsbiIsIkxoeDUiKSwgIkwyLzMiPWMoIlNhdGIyIiwiQ3V4MSIpLCAiTDQiPWMoIlJvcmIiLCJTb3g1IikgLCAiTDUvNiI9YygiQmNsMTFiIiwiRmV6ZjIiKSAsICJQcm9nIj1jKCJWaW0iLCJIZXMxIikpCiNvYmpFMTcgPSByZWFkUkRTKGZpbGUgPSBwYXN0ZShpbnB1dF9kaXIsIkUxNy41X2NvcnRleC5jb3Rhbi5SRFMiLCBzZXAgPSAiIikpCm9iakUxNyA9IHJlYWRSRFMoZmlsZSA9IHBhc3RlKGlucHV0X2RpciwiRTE3X2NvcnRleF9jbDIuY290YW4uUkRTIiwgc2VwID0gIiIpKQpgYGAKCmBgYHtyfQpnLnNwYWNlID0gZ2V0LmdlbmUuY29leHByZXNzaW9uLnNwYWNlKG9iakUxNywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbi5nZW5lcy5mb3IubWFya2VyID0gMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeS5tYXJrZXJzID0gdW5saXN0KGxheWVycykpCmBgYApgYGB7cn0KZy5zcGFjZSA9IGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGcuc3BhY2UpKQoKY29leC5wY2EuZ2VuZXMgPC0gcHJjb21wKHQoZy5zcGFjZSksCiAgICAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICBzY2FsZS4gPSBGKSAKCmZ2aXpfZWlnKGNvZXgucGNhLmdlbmVzLCBhZGRsYWJlbHM9VFJVRSxuY3AgPSAxMCkKI2Z2aXpfZWlnKGNvZXgucGNhLmdlbmVzLCBjaG9pY2UgPSAiZWlnZW52YWx1ZSIsIGFkZGxhYmVscz1UUlVFKQpgYGAKSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmhjLm5vcm0gPSBoY2x1c3QoZGlzdChnLnNwYWNlKSwgbWV0aG9kID0gIndhcmQuRDIiKQoKZGVuZCA8LSBhcy5kZW5kcm9ncmFtKGhjLm5vcm0pCgpwY2FfMSA9IGFzLmRhdGEuZnJhbWUoY29leC5wY2EuZ2VuZXMkcm90YXRpb25bLDE6MTBdKQpwY2FfMSA9IHBjYV8xW29yZGVyLmRlbmRyb2dyYW0oZGVuZCksXQoKY3V0ID0gY3V0cmVlKGhjLm5vcm0sIGsgPSA3LCBvcmRlcl9jbHVzdGVyc19hc19kYXRhID0gRikKCiMtIE5leHQgbGluZXMgYXJlIG9ubHkgdG8gY29sb3IgYW5kIHBsb3QgdGhlIHNlY29uZGFyeSBtYXJrZXJzCgp0bXAgPSBnZXQucHZhbChvYmplY3QgPSBvYmpFMTcsZ2VuZS5zZXQuY29sID11bmxpc3QobGF5ZXJzKSxnZW5lLnNldC5yb3cgPSBjb2xuYW1lcyhnLnNwYWNlKSkKZm9yIChtIGluIHVubGlzdChsYXllcnMpKSB7CiAgdG1wID0gYXMuZGF0YS5mcmFtZSh0bXBbb3JkZXIodG1wWyxtXSksXSkKICB0bXAkcmFuayA9IGMoMTpucm93KHRtcCkpCiAgY29sbmFtZXModG1wKVtuY29sKHRtcCldID0gcGFzdGUoInJhbmsiLG0sc2VwID0gIi4iKQogIH0KcmFuay5nZW5lcyA9IHRtcFssKGxlbmd0aCh1bmxpc3QobGF5ZXJzKSkrMSk6bmNvbCh0bXApXQpmb3IgKGMgaW4gYygxOmxlbmd0aChjb2xuYW1lcyhyYW5rLmdlbmVzKSkpKSB7CiAgY29sbmFtZXMocmFuay5nZW5lcylbY10gPXN0cnNwbGl0KGNvbG5hbWVzKHJhbmsuZ2VuZXMpW2NdLCBzcGxpdD0nLicsZml4ZWQgPSBUKVtbMV1dWzJdCn0KCkwxID0gcm93U3VtcyhyYW5rLmdlbmVzWyxsYXllcnNbWzFdXV0pCkwxW2xheWVyc1tbMV1dXSA9IDEKTDIgPSByb3dTdW1zKHJhbmsuZ2VuZXNbLGxheWVyc1tbMl1dXSkKTDJbbGF5ZXJzW1syXV1dID0gMQpMNCA9IHJvd1N1bXMocmFuay5nZW5lc1ssbGF5ZXJzW1szXV1dKQpMNFtsYXllcnNbWzNdXV0gPSAxCkw1ID1yb3dTdW1zKHJhbmsuZ2VuZXNbLGxheWVyc1tbNF1dXSkKTDVbbGF5ZXJzW1s0XV1dID0gMQpQID0gcm93U3VtcyhyYW5rLmdlbmVzWyxsYXllcnNbWzVdXV0pClBbbGF5ZXJzW1s1XV1dID0gMQpjb2wuc2Vjb25kYXJ5ID0gbWVyZ2UoTDEsTDIsYnk9InJvdy5uYW1lcyIsYWxsLng9VFJVRSkKY29sbmFtZXMoY29sLnNlY29uZGFyeSlbMjozXSA9IGMoIkwxIiwiTDIiKQpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LEw0LGJ5PSJyb3cubmFtZXMiLGFsbC54PVRSVUUpCmNvbG5hbWVzKGNvbC5zZWNvbmRhcnkpW25jb2woY29sLnNlY29uZGFyeSldID0gIkw0Igpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LEw1LGJ5PSJyb3cubmFtZXMiLGFsbC54PVRSVUUpCmNvbG5hbWVzKGNvbC5zZWNvbmRhcnkpW25jb2woY29sLnNlY29uZGFyeSldID0gIkw1Igpyb3duYW1lcyhjb2wuc2Vjb25kYXJ5KSA9IGNvbC5zZWNvbmRhcnkkUm93Lm5hbWVzCmNvbC5zZWNvbmRhcnkgPSBjb2wuc2Vjb25kYXJ5WywyOm5jb2woY29sLnNlY29uZGFyeSldCmNvbC5zZWNvbmRhcnkgPSBtZXJnZShjb2wuc2Vjb25kYXJ5LFAsYnk9InJvdy5uYW1lcyIsYWxsLng9VFJVRSkKY29sbmFtZXMoY29sLnNlY29uZGFyeSlbbmNvbChjb2wuc2Vjb25kYXJ5KV0gPSAiUCIKcm93bmFtZXMoY29sLnNlY29uZGFyeSkgPSBjb2wuc2Vjb25kYXJ5JFJvdy5uYW1lcwpjb2wuc2Vjb25kYXJ5ID0gY29sLnNlY29uZGFyeVssMjpuY29sKGNvbC5zZWNvbmRhcnkpXQoKIyAgdGhpcyBwYXJ0IGlzIHRvIGNoZWNrIHRoYXQgd2Ugd2lsbCBjb2xvciBhcyBzZWNvbmRhcnkgbWFya2VycyBvbmx5IHRoZSBnZW5lcyBsaW5rZWQgdG8gdGhlCiMgcHJpbWFyeSB3aXRoIHBvc2l0aXZlIGNvZXgKdGVtcC5jb2V4ID0gYXMubWF0cml4KG9iakUxN0Bjb2V4W3Jvd25hbWVzKG9iakUxN0Bjb2V4KSAlaW4lIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzKG9iakUxN0Bjb2V4KSAlaW4lIHVubGlzdChsYXllcnMpXSkKZm9yIChuIGluIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpKSB7CiAgaWYoYW55KHRlbXAuY29leFtuLGMoIlJlbG4iLCJMaHg1IildIDwgMCkpewogICAgY29sLnNlY29uZGFyeVtuLCJMMSJdID0gMTAwMDAwCiAgfQogIGlmKGFueSh0ZW1wLmNvZXhbbixjKCJDdXgxIiwiU2F0YjIiKV0gPCAwKSl7CiAgICBjb2wuc2Vjb25kYXJ5W24sIkwyIl0gPSAxMDAwMDAKICB9CiAgaWYoYW55KHRlbXAuY29leFtuLGMoIlJvcmIiLCJTb3g1IildIDwgMCkpewogICAgY29sLnNlY29uZGFyeVtuLCJMNCJdID0gMTAwMDAwCiAgfQogIGlmKGFueSh0ZW1wLmNvZXhbbixjKCJCY2wxMWIiLCJGZXpmMiIpXSA8IDApKXsKICAgIGNvbC5zZWNvbmRhcnlbbiwiTDUiXSA9IDEwMDAwMAogIH0KICBpZihhbnkodGVtcC5jb2V4W24sYygiVmltIiwiSGVzMSIpXSA8IDApKXsKICAgIGNvbC5zZWNvbmRhcnlbbiwiUCJdID0gMTAwMDAwCiAgfQp9CgpteWxpc3QubmFtZXMgPC0gYygiTDEiLCAiTDIiLCAiTDQiLCJMNSIsIlAiKQpwb3MubGluayAgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKG15bGlzdC5uYW1lcykpCm5hbWVzKHBvcy5saW5rKSA8LSBteWxpc3QubmFtZXMKZm9yIChnIGluIHJvd25hbWVzKGNvbC5zZWNvbmRhcnkpKSB7CiAgaWYobGVuZ3RoKCB3aGljaChjb2wuc2Vjb25kYXJ5W2csXSA9PSBtaW4oY29sLnNlY29uZGFyeVtnLF0pKSkgPT0gMSApewogIHBvcy5saW5rW1t3aGljaChjb2wuc2Vjb25kYXJ5W2csXSA9PSBtaW4oY29sLnNlY29uZGFyeVtnLF0pKSBdXSA9IAogICAgYyhwb3MubGlua1tbd2hpY2goY29sLnNlY29uZGFyeVtnLF0gPT0gbWluKGNvbC5zZWNvbmRhcnlbZyxdKSkgXV0sIGcpCiAgfQp9CiMgLS0tLQoKCnBjYV8xJGhpZ2hsaWdodCA9IHdpdGgocGNhXzEsIAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJEw1LCAiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiwKICAgICAgICAgIGlmZWxzZShyb3duYW1lcyhwY2FfMSkgJWluJSBwb3MubGluayRMMiAsICJnZW5lcyByZWxhdGVkIHRvIEwyLzMiLAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJFAgLCAiZ2VuZXMgcmVsYXRlZCB0byBQcm9nIiAsCiAgICAgICAgICBpZmVsc2Uocm93bmFtZXMocGNhXzEpICVpbiUgcG9zLmxpbmskTDEgLCAiZ2VuZXMgcmVsYXRlZCB0byBMMSIgLAogICAgICAgICAgaWZlbHNlKHJvd25hbWVzKHBjYV8xKSAlaW4lIHBvcy5saW5rJEw0ICwiZ2VuZXMgcmVsYXRlZCB0byBMNCIgLAogICAgICAibm90IG1hcmtlZCIpKSkpKSkKCiMgQnV0IHNvcnQgdGhlbSBiYXNlZCBvbiB0aGVpciBvcmRlciBpbiBkZW5kOgojY29sb3JzX3RvX3VzZSA8LSBwY2FfMSRoaWdobGlnaHRbb3JkZXIuZGVuZHJvZ3JhbShkZW5kKV0KCiNteWNvbG91cnMgPC0gYygiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiA9ICIjM0M1NDg4RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwyLzMiPSIjRjM5QjdGRkYiLCJnZW5lcyByZWxhdGVkIHRvIFByb2ciPSIjNERCQkQ1RkYiLCJnZW5lcyByZWxhdGVkIHRvIEwxIj0iI0U2NEIzNUZGIiwiZ2VuZXMgcmVsYXRlZCB0byBMNCIgPSAiIzkxRDFDMkZGIiwgIm5vdCBtYXJrZWQiPSIjQjA5Qzg1RkYiKQpwY2FfMSRoY2x1c3QgPSBjdXQKCnBjYV8xJGNvbG9ycyA9IE5BCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBMNS82IiwgImNvbG9ycyJdID0gIiMzQzU0ODhGRiIKcGNhXzFbcGNhXzEkaGlnaGxpZ2h0ID09ICJnZW5lcyByZWxhdGVkIHRvIEwyLzMiLCJjb2xvcnMiXSA9ICIjRjM5QjdGRkYiCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBQcm9nIiwiY29sb3JzIl0gPSAiIzREQkJENUZGIgpwY2FfMVtwY2FfMSRoaWdobGlnaHQgPT0gImdlbmVzIHJlbGF0ZWQgdG8gTDEiLCJjb2xvcnMiXSA9ICIjRTY0QjM1RkYiCnBjYV8xW3BjYV8xJGhpZ2hsaWdodCA9PSAiZ2VuZXMgcmVsYXRlZCB0byBMNCIsImNvbG9ycyJdID0gIiM5MUQxQzJGRiIKcGNhXzFbcGNhXzEkaGlnaGxpZ2h0ID09ICJub3QgbWFya2VkIiwiY29sb3JzIl0gPSAiI0IwOUM4NUZGIgoKCgoKZGVuZCA9YnJhbmNoZXNfY29sb3IoZGVuZCxrPTcsY29sPWMoIiM0REJCRDVGRiIsIiM5MUQxQzJGRiIsIiNFNjRCMzVGRiIsImdyYXk4MCIsIiMzQzU0ODhGRiIsIiNGMzlCN0ZGRiIsImdyYXk4MCIgKSxncm91cExhYmVscyA9IFQpCmRlbmQgPWNvbG9yX2xhYmVscyhkZW5kLGs9NyxsYWJlbHMgPSByb3duYW1lcyhwY2FfMSksY29sPXBjYV8xJGNvbG9ycykKCgpkZW5kICU+JQogIGRlbmRleHRlbmQ6OnNldCgibGFiZWxzIiwgaWZlbHNlKGxhYmVscyhkZW5kKSAlaW4lIHJvd25hbWVzKHBjYV8xKVtyb3duYW1lcyhwY2FfMSkgJWluJSBjb2xuYW1lcyhnLnNwYWNlKV0gLGxhYmVscyhkZW5kKSwiIikpICU+JQogICMgIHNldCgiYnJhbmNoZXNfa19jb2xvciIsIHZhbHVlID0gYygiZ3JheTgwIiwiIzREQkJENUZGIiwiIzkxRDFDMkZGIiAsImdyYXk4MCIsIiNGMzlCN0ZGRiIsIiNFNjRCMzVGRiIsIiMzQzU0ODhGRiIpLCBrID0gNykgJT4lCiBwbG90KGhvcml6PUYsIGF4ZXM9VCx5bGltID0gYygwLDgwKSkKYGBgCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmNsdXN0ZXIgPSBjdXQKY2x1c3RlcltjbHVzdGVyID09IDFdID0gIiM0REJCRDVGRiIKY2x1c3RlcltjbHVzdGVyID09IDJdID0gIiM5MUQxQzJGRiIKY2x1c3RlcltjbHVzdGVyID09IDNdID0gICIjRTY0QjM1RkYiCmNsdXN0ZXJbY2x1c3RlciA9PSA0XSA9ICIjQjA5Qzg1RkYiCmNsdXN0ZXJbY2x1c3RlciA9PSA1XSA9ICIjM0M1NDg4RkYiCmNsdXN0ZXJbY2x1c3RlciA9PSA2XSA9ICIjRjM5QjdGRkYiCmNsdXN0ZXJbY2x1c3RlciA9PSA3XSA9ICIjQjA5Qzg1RkYiCgoKcGxvdC5uZXcoKQpwbG90KGRlbmQsaG9yaXo9VCwgYXhlcz1ULHhsaW0gPSBjKDEwMCwwKSxsZWFmbGFiID0gIm5vbmUiKQphYmxpbmUodiA9IDQ3LCBsdHkgPSAyKQpjb2xvcmVkX2JhcnMoY2x1c3RlcixkZW5kLGhvcml6ID0gVCxzb3J0X2J5X2xhYmVsc19vcmRlciA9IEYseV9zaGlmdCA9IDEsCiAgICAgICAgICAgICAgIHJvd0xhYmVscz0gIiIgKQpncmlkR3JhcGhpY3M6OmdyaWQuZWNobygpCnRyZWUgPC0gZ3JpZC5ncmFiKCkKYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD03fQpwYXJfcGNhID0gcGNhXzFbY29sbmFtZXMoZy5zcGFjZSlbY29sbmFtZXMoZy5zcGFjZSkgJWluJSByb3duYW1lcyhwY2FfMSldLF0KI3Bsb3QgTiAxCnAxIDwtIGdncGFyYWdyYXBoKHRleHQgPSBwYXN0ZTAocm93bmFtZXMocGFyX3BjYVtwYXJfcGNhJGhjbHVzdCA9PSBwYXJfcGNhWyJGZXpmMiIsImhjbHVzdCJdLF0pLCBjb2xsYXBzZSA9ICIsICIpLCAKICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICAgc2l6ZSA9MTAsIAogICAgICAgICAgICAgICAgICBjb2xvciA9ICIjM0M1NDg4RkYiKQoKI3Bsb3QgTiAyCnAyID0gZ2dwYXJhZ3JhcGgodGV4dCA9IHBhc3RlMChyb3duYW1lcyhwYXJfcGNhW3Bhcl9wY2EkaGNsdXN0ID09IHVuaXF1ZShwYXJfcGNhJGhjbHVzdClbIXVuaXF1ZShwYXJfcGNhJGhjbHVzdCkgJWluJSB1bmlxdWUocGFyX3BjYVt1bmxpc3QobGF5ZXJzKSwiaGNsdXN0Il0pXVsxXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICBzaXplID0xMCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiZ3JheSIpCgojcGxvdCBOIDMKcDMgPSBnZ3BhcmFncmFwaCh0ZXh0ID0gcGFzdGUwKHJvd25hbWVzKHBhcl9wY2FbcGFyX3BjYSRoY2x1c3QgPT0gdW5pcXVlKHBhcl9wY2EkaGNsdXN0KVshdW5pcXVlKHBhcl9wY2EkaGNsdXN0KSAlaW4lIHVuaXF1ZShwYXJfcGNhW3VubGlzdChsYXllcnMpLCJoY2x1c3QiXSldWzJdLF0pLCBjb2xsYXBzZSA9ICIsICIpLCAKICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIAogICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICBjb2xvciA9ICJncmF5IikKI3Bsb3QgTiA0CnA0ID0gZ2dwYXJhZ3JhcGgodGV4dCA9IHBhc3RlMChyb3duYW1lcyhwYXJfcGNhW3Bhcl9wY2EkaGNsdXN0ID09IHBhcl9wY2FbIlJlbG4iLCJoY2x1c3QiXSxdKSwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICBzaXplID0xMCwgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAiI0U2NEIzNUZGIikKCiNwbG90IE4gNQpwNSA9IGdncGFyYWdyYXBoKHRleHQgPSBwYXN0ZTAocm93bmFtZXMocGFyX3BjYVtwYXJfcGNhJGhjbHVzdCA9PSBwYXJfcGNhWyJDdXgxIiwiaGNsdXN0Il0sXSksIGNvbGxhcHNlID0gIiwgIiksIAogICAgICAgICAgICAgICAgIGZhY2UgPSAiaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgc2l6ZSA9MTAsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gcGFyX3BjYVsiQ3V4MSIsImNvbG9ycyJdKQojcGxvdCBOIDYKcDYgPSBnZ3BhcmFncmFwaCh0ZXh0ID0gcGFzdGUwKHJvd25hbWVzKHBhcl9wY2FbcGFyX3BjYSRoY2x1c3QgPT0gcGFyX3BjYVsiUm9yYiIsImhjbHVzdCJdLF0pLCBjb2xsYXBzZSA9ICIsICIpLCAKICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIAogICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICBjb2xvciA9IHBhcl9wY2FbIlJvcmIiLCJjb2xvcnMiXSkKI3Bsb3QgTiA3CnA3ID0gZ2dwYXJhZ3JhcGgodGV4dCA9IHBhc3RlMChyb3duYW1lcyhwYXJfcGNhW3Bhcl9wY2EkaGNsdXN0ID09IHBhcl9wY2FbIlZpbSIsImhjbHVzdCJdLF0pLCBjb2xsYXBzZSA9ICIsICIpLCAKICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIAogICAgICAgICAgICAgICAgIHNpemUgPTEwLCAKICAgICAgICAgICAgICAgICBjb2xvciA9IHBhcl9wY2FbIlZpbSIsImNvbG9ycyJdKQoKdyA9IGdncGFyYWdyYXBoKHRleHQgPSAiICIsIAogICAgICAgICAgICAgICAgIGZhY2UgPSAiaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgc2l6ZSA9MTAsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gIndoaXRlIikKCnBwID1nZ2FycmFuZ2UocDMscDUscDEscDIscDQscDYscDcsdywKICAgICAgICAgIG5jb2wgPSAxLCBucm93ID0gOCwKICAgICAgICAgIGhlaWdodHMgPSBjKDAuMSwwLjE1LDAuMjMsIDAuMSwgMC4yLCAwLjIsIDAuMSwgMC4zNSkpCgoKICAgIApsYXkgPC0gcmJpbmQoYygxLE5BKSwKICAgICAgICAgICAgIGMoMSwyLjUpLAogICAgICAgICAgICAgYygxLDIuNSksCiAgICAgICAgICAgICBjKDEsMi41KSwKICAgICAgICAgICAgIGMoMSwyLjUpLAogICAgICAgICAgICAgYygxLDIuNSksCiAgICAgICAgICAgICBjKDEsTkEpKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UodHJlZSwgcHAsIGxheW91dF9tYXRyaXggPSBsYXkpCgpgYGAKCm9yIGp1c3Qgd2l0aCBwcmltYXJ5IG1hcmtlcnMKCmBgYHtyIGZpZy53aWR0aD0gMTB9CiMgc29tZSBtb3JlIGdlbmVzIGFzIGxhbmRtYXJrcwpjb250cm9scyA9bGlzdCgiZ2VuZXMgcmVsYXRlZCB0byBMNS82Ij1jKCJGb3hwMiIsIlRicjEiKSwgImdlbmVzIHJlbGF0ZWQgdG8gTDIvMyI9YygiTWVmMmMiKSwgImdlbmVzIHJlbGF0ZWQgdG8gUHJvZyI9YygiTmVzIiwiU294MiIpICwgImdlbmVzIHJlbGF0ZWQgdG8gTDEiPWMoKSAsICJnZW5lcyByZWxhdGVkIHRvIEw0Ij1jKCkpIApkZW5kICU+JQpkZW5kZXh0ZW5kOjpzZXQoImxhYmVscyIsIGlmZWxzZShsYWJlbHMoZGVuZCkgJWluJSByb3duYW1lcyhwY2FfMSlbcm93bmFtZXMocGNhXzEpICVpbiUgYyh1bmxpc3QobGF5ZXJzKSx1bmxpc3QoY29udHJvbHMpKV0sIGxhYmVscyhkZW5kKSwgIiIpKSAlPiUKICBkZW5kZXh0ZW5kOjpzZXQoImJyYW5jaGVzX2tfY29sb3IiLCB2YWx1ZSA9IGMoImdyYXk4MCIsIiM0REJCRDVGRiIsIiM5MUQxQzJGRiIgLCJncmF5ODAiLCIjRjM5QjdGRkYiLCIjRTY0QjM1RkYiLCIjM0M1NDg4RkYiKSwgayA9IDcpICU+JQpwbG90KGhvcml6PUYsIGF4ZXM9VCx5bGltID0gYygwLDEwMCkpCgpgYGAKCk5vdyB3ZSBjYW4gcGxvdCB0aGUgUENBCmBgYHtyfQojIGRhdGFmcmFtZSB0byBiZSBhYmxlIHRvIGxhYmVsIG9ubHkgcHJpbWFyeSBtYXJrZXJzIGFuZCBjb250cm9sIGdlbmVzCnRleHRkZiA8LSBwY2FfMVtyb3duYW1lcyhwY2FfMSkgJWluJSBjKHVubGlzdChsYXllcnMpLHVubGlzdChjb250cm9scykpICwgXQoKIGZvciAobSBpbiBjKDE6bGVuZ3RoKGNvbnRyb2xzKSkpIHsKICBmb3IgKGcgaW4gY29udHJvbHNbW21dXSkgewogICAgaWYoZyAlaW4lIHJvd25hbWVzKHRleHRkZikpewogICAgICB0ZXh0ZGZbZywiaGlnaGxpZ2h0Il0gPSBuYW1lcyhjb250cm9sc1ttXSkKICAgIH0gCiAgfQp9CgojIGRlY2lkaW5nIHRoZSBjb2xvcnMKbXljb2xvdXJzIDwtIGMoImdlbmVzIHJlbGF0ZWQgdG8gTDUvNiIgPSAiIzNDNTQ4OEZGIiwiZ2VuZXMgcmVsYXRlZCB0byBMMi8zIj0iI0YzOUI3RkZGIiwiZ2VuZXMgcmVsYXRlZCB0byBQcm9nIj0iIzREQkJENUZGIiwiZ2VuZXMgcmVsYXRlZCB0byBMMSI9IiNFNjRCMzVGRiIsImdlbmVzIHJlbGF0ZWQgdG8gTDQiID0gIiM5MUQxQzJGRiIsICJub3QgbWFya2VkIj0iI0IwOUM4NUZGIikKCiMgdG8gYXNzaW5nIGNvcnJlY2x5IHRoZSBjbHVzdGVyIG51bWJlciBhbmQgdGhlIGNvbG9yCm15Y29sb3VyczIgPSBjKCJSZWxuIiwiU2F0YjIiLCJSb3JiIiwiQmNsMTFiIiwiVmltIikKbmFtZXMobXljb2xvdXJzMikgPSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkKCm15Y29sb3VyczJbbXljb2xvdXJzMiA9PSAiUmVsbiJdID0gIiNFNjRCMzVGRiIKbXljb2xvdXJzMltteWNvbG91cnMyID09ICJTYXRiMiJdID0gIiNGMzlCN0ZGRiIKbXljb2xvdXJzMltteWNvbG91cnMyID09ICJSb3JiIl0gPSAiIzkxRDFDMkZGIgpteWNvbG91cnMyW215Y29sb3VyczIgPT0gIkJjbDExYiJdID0gIiMzQzU0ODhGRiIKbXljb2xvdXJzMltteWNvbG91cnMyID09ICJWaW0iXSA9ICIjNERCQkQ1RkYiCmNvbG9yX3RvX2FkZCA9IHVuaXF1ZShwY2FfMSRoY2x1c3QpWyF1bmlxdWUocGNhXzEkaGNsdXN0KSAlaW4lIGFzLm51bWVyaWMobmFtZXMobXljb2xvdXJzMikpXQpuYW1lcyhjb2xvcl90b19hZGQpID0gdW5pcXVlKHBjYV8xJGhjbHVzdClbIXVuaXF1ZShwY2FfMSRoY2x1c3QpICVpbiUgYXMubnVtZXJpYyhuYW1lcyhteWNvbG91cnMyKSldCmNvbG9yX3RvX2FkZFtjb2xvcl90b19hZGQgJWluJSAKICAgICAgICAgICAgICAgICB1bmlxdWUocGNhXzEkaGNsdXN0KVshdW5pcXVlKHBjYV8xJGhjbHVzdCkgJWluJSBhcy5udW1lcmljKG5hbWVzKG15Y29sb3VyczIpKV1dID0gIiNCMDlDODVGRiIKbXljb2xvdXJzMiA9IGMobXljb2xvdXJzMixjb2xvcl90b19hZGQpCgpwY2ExID0gZ2dwbG90KHN1YnNldChwY2FfMSwhaGNsdXN0ICVpbiUgdW5pcXVlKGN1dFt1bmxpc3QobGF5ZXJzKV0pICApLCBhZXMoeD1QQzEsIHk9UEMyKSkgKyAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMyxjb2xvciA9ICIjQjA5Qzg1RkYiLHNpemU9MSkKCiNwY2EyID0gcGNhMSArIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChwY2FfMSwgaGlnaGxpZ2h0ICE9ICJub3QgbWFya2VkIiApLCBhZXMoeD1QQzEsIHk9UEMyLCBjb2xvdXI9aGNsdXN0KSxzaXplPTIuNSxhbHBoYSA9IDAuOSkgCnBjYTIgPSBwY2ExICsgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHBjYV8xLCBoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkgKSwgYWVzKHg9UEMxLCB5PVBDMiwgY29sb3VyPWFzLmNoYXJhY3RlcihoY2x1c3QpKSxzaXplPTEsYWxwaGEgPSAwLjUpICsgCiBzY2FsZV9jb2xvcl9tYW51YWwoICJTdGF0dXMiLCB2YWx1ZXMgPSBteWNvbG91cnMyKSAgKwogIHNjYWxlX2ZpbGxfbWFudWFsKCAiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICB4bGFiKCIiKSArIHlsYWIoIiIpICsKICBnZW9tX2xhYmVsX3JlcGVsKGRhdGEgPXRleHRkZiAsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBsYWJlbCA9IHJvd25hbWVzKHRleHRkZiksZmlsbD1hcy5jaGFyYWN0ZXIoaGNsdXN0KSksCiAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gTkEsIAogICAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiLAogICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0KSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID10ZXh0ZGYgLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgbGFiZWwgPSByb3duYW1lcyh0ZXh0ZGYpKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSBOQSwgCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gJ2JsYWNrJywKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJib3RoIiwKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSwgCiAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQpICsKICBnZ3RpdGxlKCJQQ0EiKSArCiAgdGhlbWVfbGlnaHQoYmFzZV9zaXplPTEwKSArCiAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZT0iaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSIjM0M1NDg4RkYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0wLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0PTEuMixtYXJnaW4gPSBtYXJnaW4odCA9IDUsIGIgPSAtMTUpKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyB0aXRsKQoKcGNhMiAjKyBnZW9tX2VuY2lyY2xlKGRhdGEgPSBwY2FfMSwgYWVzKGdyb3VwPWhjbHVzdCkpIApgYGAKCnQtU05FIGNvZGUgYW5kIHBsb3QKCmBgYHtyfQojIHJ1biB0aGUgdC1TTkUKY2wuZ2VuZXMudHNuZSA9IFJ0c25lKGcuc3BhY2UgLGluaXRpYWxfZGltcyA9IDEwMCwgZGltcyA9IDIsIHBlcnBsZXhpdHk9MzAsZXRhID0gMjAwLCB2ZXJib3NlPUYsIG1heF9pdGVyID0gMzAwMCx0aGV0YT0wLjQsbnVtX3RocmVhZHMgPSAxMCxwY2FfY2VudGVyID0gVCwgcGNhX3NjYWxlID0gRkFMU0UsIG5vcm1hbGl6ZSA9IFQgKQoKZF90c25lXzEgPSBhcy5kYXRhLmZyYW1lKGNsLmdlbmVzLnRzbmUkWSkKcm93bmFtZXMoZF90c25lXzEpID0gcm93bmFtZXMoZy5zcGFjZSkKCmRfdHNuZV8xID0gZF90c25lXzFbb3JkZXIuZGVuZHJvZ3JhbShkZW5kKSxdCgojIHNhdmUgdGhlIGNsdXN0ZXIgbnVtZWJyIGluc2lkZSBhIGRhdGFmcmFtZSB3aXRoIHRoZSB0LVNORSBpbmZvcm1hdGlvbgpkX3RzbmVfMSRoY2x1c3QgPSBjdXQKCmRfdHNuZV8xJG5hbWVzID0gcm93bmFtZXMoZF90c25lXzEpCgojIGFzIGJlZm9yZSB0byBsYWJlbCBvbmx5IHNvbWUgZ2VuZXMKdGV4dGRmIDwtIGRfdHNuZV8xW3Jvd25hbWVzKGRfdHNuZV8xKSAlaW4lIGModW5saXN0KGxheWVycyksdW5saXN0KGNvbnRyb2xzKSksXQoKZm9yIChtIGluIGMoMTpsZW5ndGgoY29udHJvbHMpKSkgewogIGZvciAoZyBpbiBjb250cm9sc1tbbV1dKSB7CiAgICBpZihnICVpbiUgcm93bmFtZXModGV4dGRmKSl7CiAgICAgIHRleHRkZltnLCJoaWdobGlnaHQiXSA9IG5hbWVzKGNvbnRyb2xzW21dKQogICAgfSAKICB9Cn0KCgogcDEgPSBnZ3Bsb3Qoc3Vic2V0KGRfdHNuZV8xLCFoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkpLCBhZXMoeD1WMSwgeT1WMikpICsgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMsIGNvbG9yID0gIiNCMDlDODVGRiIsIHNpemU9MSkKCnAyID0gcDEgKyBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZF90c25lXzEsIGhjbHVzdCAlaW4lIHVuaXF1ZShjdXRbdW5saXN0KGxheWVycyldKSApLCBhZXMoeD1WMSwgeT1WMiwgY29sb3VyPWFzLmNoYXJhY3RlcihoY2x1c3QpKSxzaXplPTEsYWxwaGEgPSAwLjUpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICBzY2FsZV9maWxsX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICB4bGFiKCIiKSArIHlsYWIoIiIpKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9dGV4dGRmICwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IG5hbWVzLGZpbGw9YXMuY2hhcmFjdGVyKGhjbHVzdCkpLAogICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BLCAKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCAKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJib3RoIiwKICAgICAgICAgICAgICAgICAgIG5hLnJtPVRSVUUsCiAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNCkgKwogIGdlb21fbGFiZWxfcmVwZWwoZGF0YSA9dGV4dGRmICwgYWVzKHggPSBWMSwgeSA9IFYyLCBsYWJlbCA9IG5hbWVzKSwKICAgICAgICAgICAgICAgICAgIGxhYmVsLnNpemUgPSBOQSwgCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gJ2JsYWNrJywKICAgICAgICAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuNSwKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJib3RoIiwKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSwgCiAgICAgICAgICAgICAgICAgICBuYS5ybT1UUlVFLAogICAgICAgICAgICAgICAgICAgZmlsbCA9IE5BLAogICAgICAgICAgICAgICAgICAgc2VlZCA9IDEyMzQpICsKICBnZ3RpdGxlKCJ0LVNORSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZT0iaXRhbGljIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSIjM0M1NDg4RkYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0wLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0PTEuMixtYXJnaW4gPSBtYXJnaW4odCA9IDUsIGIgPSAtMTUpKSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyB0aXRsKQpwMgpgYGAKQ29kZSB0byBjcmVhdGUgYW4gaXRlcmFjdGl2ZSBwbG90LiBUaGlzIGNhbiBiZSBtb2RpZmllZCB0byBiZSB1c2VkIHdpdGggYWxsIHRoZSBwbG90cy4KCmBgYHtyIGVjaG89VFJVRX0KCnAgPSBnZ3Bsb3QoZF90c25lXzEsIGFlcyh4PVYxLCB5PVYyLCB0ZXh0PSBwYXN0ZSgiZ2VuZTogIixuYW1lcykpKSArICAKICBnZW9tX3BvaW50KHNpemU9MiwgYWVzKGNvbG91cj1hcy5jaGFyYWN0ZXIoaGNsdXN0KSksIGFscGhhPTAuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgiU3RhdHVzIiwgdmFsdWVzID0gbXljb2xvdXJzMikgICsKICB4bGFiKCIiKSArIHlsYWIoIiIpICsKICBnZ3RpdGxlKCJ0LVNORSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKQoKZ2dwbG90bHkocCkKYGBgCgpNdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgKE1EUykgYW5kIHBsb3QKYGBge3J9CiMgcnVuIHRoZSBNRFMKZ2VuZXMuZGlzdC5ldWMgPSAgZGlzdChnLnNwYWNlLCBtZXRob2QgPSAgImV1Y2xpZGVhbiIpCiNmaXQgPC0gaXNvTURTKGdlbmVzLmRpc3QuZXVjKSAjIG5vdCBsaW5lYXIKZml0IDwtIGlzb01EUyhnZW5lcy5kaXN0LmV1YykKCmZpdC5nZW5lcyA9IGFzLmRhdGEuZnJhbWUoZml0JHBvaW50cykKCmZpdC5nZW5lcyA9IGZpdC5nZW5lc1tvcmRlci5kZW5kcm9ncmFtKGRlbmQpLF0KCmZpdC5nZW5lcyRoY2x1c3QgPSBjdXQKCgpmaXQuZ2VuZXMkbmFtZXMgPSByb3duYW1lcyhmaXQuZ2VuZXMpCgpteWNvbG91cnMzIDwtIGMoImNsdXN0ZXIgTDUvNiBtYXJrZXJzIiA9ICIjM0M1NDg4RkYiLCJjbHVzdGVyIEwyLzMgbWFya2VycyI9IiNGMzlCN0ZGRiIsImNsdXN0ZXIgUHJvZyBtYXJrZXJzIj0iIzREQkJENUZGIiwiY2x1c3RlciBMMSBtYXJrZXJzIj0iI0U2NEIzNUZGIiwiY2x1c3RlciBMNCBtYXJrZXJzIiA9ICIjOTFEMUMyRkYiLCAibm90IGlkZW50aWZpZWQgY2x1c3RlciI9IiNCMDlDODVGRiIpCgojbXljb2xvdXJzMyA8LSBjKCJjbHVzdGVyIGxheWVyIFYtVkkgbWFya2VycyIgPSAiIzNDNTQ4OEZGIiwiY2x1c3RlciBsYXllciBJSS1JSUkgbWFya2VycyI9IiNGMzlCN0ZGRiIsImNsdXN0ZXIgcHJvZ2VuaXRvciBtYXJrZXJzIj0iIzREQkJENUZGIiwiY2x1c3RlciBsYXllciBJIG1hcmtlcnMiPSIjRTY0QjM1RkYiLCJjbHVzdGVyIGxheWVyIElWIG1hcmtlcnMiID0gIiM5MUQxQzJGRiIsICJub3QgaWRlbnRpZmllZCBjbHVzdGVyIj0iI0IwOUM4NUZGIikKCgojZml0LmdlbmVzJGhjbHVzdCA9IGZhY3RvcihjdXRyZWUoaGMubm9ybSwgNykpCnVzZWQgPSB2ZWN0b3IoKQpmb3IgKGsgaW4gYygxOmxlbmd0aChsYXllcnMpKSkgewogICNwcmludChrKQogIHR0ID1hcy5udW1lcmljKGN1dFtsYXllcnNbW2tdXV1bMV0pCiAgZml0LmdlbmVzW2ZpdC5nZW5lcyRoY2x1c3QgPT0gdHQsImNsdXN0ZXIiXSA9IHBhc3RlKCJjbHVzdGVyIixuYW1lcyhsYXllcnNba10pLCJtYXJrZXJzIiwgc2VwID0gIiAiICkKICB1c2VkID0gYyh1c2VkLGN1dFtsYXllcnNbW2tdXV1bMV0pCn0KCmZpdC5nZW5lc1tmaXQuZ2VuZXMkaGNsdXN0ICVpbiUgKHVuaXF1ZShmaXQuZ2VuZXMkaGNsdXN0KVshdW5pcXVlKGZpdC5nZW5lcyRoY2x1c3QpICVpbiUgdXNlZF0pLF0kY2x1c3RlciA9ICJub3QgaWRlbnRpZmllZCBjbHVzdGVyIgoKdGV4dGRmIDwtIGZpdC5nZW5lc1tyb3duYW1lcyhmaXQuZ2VuZXMpICVpbiUgYyh1bmxpc3QobGF5ZXJzKSx1bmxpc3QoY29udHJvbHMpKSxdCgogICBmMSA9IGdncGxvdChzdWJzZXQoZml0LmdlbmVzLCFoY2x1c3QgJWluJSB1bmlxdWUoY3V0W3VubGlzdChsYXllcnMpXSkgKSwgYWVzKHg9VjEsIHk9VjIpKSArICBnZW9tX3BvaW50KGFscGhhID0gMC4zLCBjb2xvciA9ICIjQjA5Qzg1RkYiLCBzaXplPTEpCgpmMiA9IGYxICsgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGZpdC5nZW5lcywgaGNsdXN0ICVpbiUgdW5pcXVlKGN1dFt1bmxpc3QobGF5ZXJzKV0pICksIAogICAgICAgICAgICAgICAgICAgICBhZXMoeD1WMSwgeT1WMiwgY29sb3VyPWNsdXN0ZXIpLCBzaXplPTEsYWxwaGEgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9tYW51YWwoIlN0YXR1cyIsIHZhbHVlcyA9IG15Y29sb3VyczMsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkxheWVyIEkgY2x1c3RlciAiLCJMYXllcnMgSUkvSUlJIGNsdXN0ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXllciBJViBjbHVzdGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGF5ZXJzIFYvVkkgY2x1c3RlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByb2dlbml0b3JzIGNsdXN0ZXIiKSApICArCiAgc2NhbGVfZmlsbF9tYW51YWwoIlN0YXR1cyIsIHZhbHVlcyA9IG15Y29sb3VyczMpICArIAogIHhsYWIoIiIpICsgeWxhYigiIikrCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID10ZXh0ZGYgLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGxhYmVsID0gcm93bmFtZXModGV4dGRmKSxmaWxsPWNsdXN0ZXIpLAogICAgICAgICAgICAgICAgICAgbGFiZWwuc2l6ZSA9IE5BLCAKICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCAKICAgICAgICAgICAgICAgICAgIGRpcmVjdGlvbiA9ImJvdGgiLAogICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID10ZXh0ZGYgLCBhZXMoeCA9IFYxLCB5ID0gVjIsIGxhYmVsID0gcm93bmFtZXModGV4dGRmKSksCiAgICAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gTkEsIAogICAgICAgICAgICAgICAgICAgc2VnbWVudC5jb2xvciA9ICdibGFjaycsCiAgICAgICAgICAgICAgICAgICBzZWdtZW50LnNpemUgPSAwLjUsCiAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYm90aCIsCiAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIAogICAgICAgICAgICAgICAgICAgbmEucm09VFJVRSwKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwKICAgICAgICAgICAgICAgICAgIHNlZWQgPSAxMjM0LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2d0aXRsZSgiTURTIikgKwogIHRoZW1lX2xpZ2h0KGJhc2Vfc2l6ZT0xMCkgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlPSJpdGFsaWMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9IiMzQzU0ODhGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PTAuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVoZWlnaHQ9MS4yLG1hcmdpbiA9IG1hcmdpbih0ID0gNSwgYiA9IC0xNSkpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiIzNDNTQ4OEZGIixmYWNlID0iaXRhbGljIiApLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAgIyB0aXRsKQoKCmYyICsgc2NhbGVfeV9yZXZlcnNlKCkrIHNjYWxlX3hfcmV2ZXJzZSgpIysgZ2VvbV9lbmNpcmNsZShkYXRhID0gZml0LmdlbmVzLCBhZXMoZ3JvdXA9YDVfY2x1c3RlcnNgKSkgCmBgYApgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJBbm5vdGF0aW9uRGJpIikKbGlicmFyeSgib3JnLk1tLmVnLmRiIikKbGlicmFyeSgiR08uZGIiKQoKYGBgCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CkdPID0gIkdPOjAwMDM2NzYiICNudWNsZWljIGFjaWQgYmluZGluZwpsaWJyYXJ5KG9yZy5NbS5lZy5kYikKbGlzdCA9IHNlbGVjdChvcmcuTW0uZWcuZGIsIGtleXMgPSByb3duYW1lcyhwY2FfMSksCiAgICAgICAgICAgICAgY29sdW1ucz1jKCJTWU1CT0wiLCJHT0FMTCIpLGtleXR5cGU9IlNZTUJPTCIpICNbZml0LmdlbmVzJGhjbHVzdCAlaW4lIGMoNyw0LDEsMiwzKSxdKSwKbGlzdCA9IGxpc3RbbGlzdCRHT0FMTCA9PSBHTyxdCmxpc3QgPSBsaXN0W2NvbXBsZXRlLmNhc2VzKGxpc3QpLF0Kbm90LnVzZWZ1bCA9IGMoIk5BUyIsIlRBUyIsIklFQSIsIklDIiwiTkQiKQpsaXN0MSA9IGxpc3RbIWxpc3QkRVZJREVOQ0VBTEwgJWluJSBub3QudXNlZnVsLF0KbGlzdDEgPSB1bmlxdWUobGlzdDEkU1lNQk9MKQpwY2FfMSRHTyA9IE5BCnBjYV8xW2xpc3QxLF0kR08gPSAibnVjbGVpYyBhY2lkIGJpbmRpbmciCnBjYV8xW2xpc3QxLGMoMTE6bmNvbChwY2FfMSkpXQpgYGAKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=